/**
 * @example examples/wireless/ble/sample_gatt_server/main/xf_main.c
 * xf_wal ble gatt 服务端 示例。
 */

/* ==================== [Includes] ========================================== */

#include "xf_hal.h"
#include "xf_ble_gap.h"
#include "xf_ble_gatt_server.h"
#include "string.h"

/* ==================== [Defines] =========================================== */

#define TAG "sample_gatts"

#define DEFAULT_BLE_GAP_ADV_MIN_INTERVAL 0x30   // BLE GAP 广播最小间隔
#define DEFAULT_BLE_GAP_ADV_MAX_INTERVAL 0x60   // BLE GAP 广播最大间隔
#define DEFAULT_BLE_GAP_ADV_DURATION_FOREVER 0  // BLE GAP 广播持续时间: 永久

#define DEFAULT_SERVICE_UUID        (0x6677)    // 服务 UUID
#define DEFAULT_CHARA_READ_UUID     (0xAABB)    // 特征 UUID , 用于读
#define DEFAULT_CHARA_WRITE_UUID    (0xCCDD)    // 特征 UUID , 用于写

/* ==================== [Typedefs] ========================================== */

/* ==================== [Static Prototypes] ================================= */

static xf_err_t sample_ble_gap_event_cb(
    xf_ble_gap_evt_t event,
    xf_ble_gap_evt_cb_param_t *param);

static xf_err_t sample_ble_gatts_event_cb(
    xf_ble_gatts_evt_t event,
    xf_ble_gatts_evt_cb_param_t *param);

/* ==================== [Static Variables] ================================== */

static uint8_t s_adv_id = XF_BLE_ADV_ID_INVALID;
static xf_ble_uuid_info_t s_app_uuid = XF_BLE_UUID16_INIT(0x0000);
static uint8_t s_app_id = XF_BLE_GATT_APP_ID_INVALID;

uint8_t sample_gatts_device_name[] = "XF_GATTS";
uint8_t sample_adv_data[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
uint8_t sample_adv_rsp_data[] = {0x11, 0x00, 0x00, 0x00, 0x00, 0x66};

static uint8_t read_req_indication[] = "REQ_READ RSP";
static uint8_t write_req_indication[] = "REQ_WRITE RSP";

static xf_ble_gap_adv_param_t s_adv_param = {
    /* 广播间隔 [N * 0.625ms] */
    .min_interval = DEFAULT_BLE_GAP_ADV_MIN_INTERVAL,
    .max_interval = DEFAULT_BLE_GAP_ADV_MAX_INTERVAL,

    // 广播类型
    .type = XF_BLE_GAP_ADV_TYPE_CONN_SCAN_UNDIR,
    .own_addr =
    {
        .type = XF_BLE_ADDR_TYPE_PUBLIC_DEV, // 本端地址类型
        .addr = {0x0},                       // 本端地址
    },
    .peer_addr =
    {
        .type = XF_BLE_ADDR_TYPE_PUBLIC_DEV, // 对端地址类型
        .addr = { 0x0 },                    // 对端地址
    },
    .channel_map = XF_BLE_GAP_ADV_CH_ALL,   // 使用37/38/39三个通道
    .filter_policy = XF_BLE_GAP_ADV_FILTER_POLICY_ANY_SCAN_ANY_CONN,
    .tx_power = 1,
};

static xf_ble_gap_adv_data_t s_adv_data = {
    .adv_struct_set = (xf_ble_gap_adv_struct_t [])
    {
        {
            .ad_type = XF_BLE_ADV_STRUCT_TYPE_LOCAL_NAME_ALL,
            .ad_data_len = sizeof(sample_gatts_device_name),
            .ad_data.local_name = sample_gatts_device_name,
            .is_ptr = true,
        },
        {
            .ad_type = XF_BLE_ADV_STRUCT_TYPE_TX_POWER_LEVEL,
            .ad_data_len = sizeof(uint8_t),
            .ad_data.adv_var.val_u8 = 18,
            .is_ptr = false,
        },
        {0}
    },
    .scan_rsp_struct_set = (xf_ble_gap_adv_struct_t [])
    {
        {
            .ad_type = XF_BLE_ADV_STRUCT_TYPE_APPEARANCE,
            .ad_data_len = sizeof(xf_ble_appearance_t),
            .ad_data.appearance = XF_BLE_APPEARANCE_GENERIC_PHONE,
            .is_ptr = false,
        },
        {0}
    },
};

static uint8_t chara_value_array[][10] = {
    {0x12, 0x34},
    {0xAA, 0xBB}
};

static xf_ble_gatts_service_t service_set[] = {
    {
        .uuid = XF_BLE_UUID16_DECLARE(DEFAULT_SERVICE_UUID),
        .type = XF_BLE_GATT_SERVICE_TYPE_PRIMARY,
        .chara_set = (xf_ble_gatts_chara_t [])
        {
            {
                .uuid = XF_BLE_UUID16_DECLARE(DEFAULT_CHARA_READ_UUID),
                .value = chara_value_array[0],
                .value_len = sizeof(chara_value_array[0]),
                .perms = XF_BLE_GATT_ATTR_PERM_READ | XF_BLE_GATT_ATTR_PERM_WRITE,
                .props = XF_BLE_GATT_CHARA_PROP_READ | XF_BLE_GATT_CHARA_PROP_NOTIFY,
            },
            {
                .uuid = XF_BLE_UUID16_DECLARE(DEFAULT_CHARA_WRITE_UUID),
                .value = chara_value_array[1],
                .value_len = sizeof(chara_value_array[1]),
                .perms = XF_BLE_GATT_ATTR_PERM_READ | XF_BLE_GATT_ATTR_PERM_WRITE,
                .props = XF_BLE_GATT_CHARA_PROP_READ | XF_BLE_GATT_CHARA_PROP_WRITE_NO_RSP 
                        | XF_BLE_GATT_CHARA_PROP_NOTIFY,
            },
            {0},    // 特征集合最后加入一个空元素，用于标记特征集合结束  
        }
    },
    {0} // 服务集合最后加入一个空元素，用于服务特征集合结束  
};

/* ==================== [Macros] ============================================ */

/* ==================== [Global Functions] ================================== */

void xf_main(void)
{
    XF_LOGI(TAG, "xf ble gatt server sample");
    xf_err_t ret = XF_OK;
    // 使能 ble
    xf_ble_enable();

    // 注册 GAP 事件回调
    ret = xf_ble_gap_event_cb_register(sample_ble_gap_event_cb, XF_BLE_EVT_ALL);
    XF_CHECK(ret != XF_OK, XF_RETURN_VOID, TAG,
             "REGISTER common event cb failed:%#X", ret);

    // 注册 gatts 事件回调
    ret = xf_ble_gatts_event_cb_register(sample_ble_gatts_event_cb, XF_BLE_EVT_ALL);
    XF_CHECK(ret != XF_OK, XF_RETURN_VOID, TAG,
             "REGISTER gatts event cb failed:%#X", ret);

    /* 设置本地名称、外观 */
    xf_ble_gap_set_local_name(sample_gatts_device_name, sizeof(sample_gatts_device_name));
    xf_ble_gap_set_local_appearance(XF_BLE_APPEARANCE_HID_DIGITIZER_TABLET);

    // 注册 gatts app_profile
    ret = xf_ble_gatts_app_register(&s_app_uuid, &s_app_id);
    XF_CHECK(ret != XF_OK || s_app_id == 0, XF_RETURN_VOID, TAG,
             "REGISTER app profile failed:%#X s_app_id:%d", ret, s_app_id);

    // 添加服务至 app_profile
    ret = xf_ble_gatts_add_service_to_app(
              s_app_id, &service_set[0]);
    XF_CHECK(ret != XF_OK, XF_RETURN_VOID, TAG,
             "ADD service failed:%#X", ret);

    // 启动服务
    ret = xf_ble_gatts_start_service(s_app_id, service_set[0].handle);
    XF_CHECK(ret != XF_OK, XF_RETURN_VOID, TAG,
             "START service failed:%#X", ret);

    /* 设置广播数据及参数，创建广播 */
    ret = xf_ble_gap_create_adv(&s_adv_id, &s_adv_param, &s_adv_data);
    XF_CHECK(ret != XF_OK, XF_RETURN_VOID, TAG,
             "Create ADV failed:%#X", ret);
    XF_LOGI(TAG, "Create ADV CMPL");

    /* 开启广播 */
    ret = xf_ble_gap_start_adv(s_adv_id, DEFAULT_BLE_GAP_ADV_DURATION_FOREVER);
    XF_CHECK(ret != XF_OK, XF_RETURN_VOID, TAG,
             "START ADV failed:%#X", ret);
    XF_LOGI(TAG, "START ADV CMPL");
}

/* ==================== [Static Functions] ================================== */

static xf_err_t sample_ble_gap_event_cb(
    xf_ble_gap_evt_t event,
    xf_ble_gap_evt_cb_param_t *param)
{
    UNUSED(s_app_id);
    UNUSED(param);
    switch (event) {
    /* 事件: 连接  */
    case XF_BLE_GAP_EVT_CONNECT: {
        XF_LOGI(TAG, "EV:peer connect:s_app_id:%d,conn_id:%d,"
                "addr_type:%d,addr:"XF_BLE_ADDR_PRINT_FMT,
                s_app_id, param->connect.conn_id,
                param->connect.peer_addr.type,
                XF_BLE_ADDR_EXPAND_TO_ARG(param->connect.peer_addr.addr));

        xf_ble_sm_authen_rqmt_t authen_rqmt = XF_BLE_SM_AUTHEN_RQMT_BOND;
        xf_ble_sm_io_cap_t io_capability = XF_BLE_SM_IO_CAP_NONE;
        xf_err_t ret = XF_OK;
        ret = xf_ble_gap_set_security_param(param->connect.conn_id,
                  XF_BLE_SM_PARAM_AUTHEN_RQMT, sizeof(authen_rqmt), &authen_rqmt);
        XF_CHECK(ret != XF_OK, ret, TAG,
                 "set_security_param failed:%#X", ret);
        ret = xf_ble_gap_set_security_param(param->connect.conn_id,
                  XF_BLE_SM_PARAM_IO_CAP_MODE, sizeof(io_capability), &io_capability);
        XF_CHECK(ret != XF_OK, ret, TAG,
                 "set_security_param failed:%#X", ret);
    } break;
    case XF_BLE_GAP_EVT_PAIR_END: {
        XF_LOGI(TAG, "EV:pair end:s_app_id:%d,conn_id:%d,"
                "addr_type:%d,addr:"XF_BLE_ADDR_PRINT_FMT,
                s_app_id, param->connect.conn_id,
                param->connect.peer_addr.type,
                XF_BLE_ADDR_EXPAND_TO_ARG(param->connect.peer_addr.addr));
    } break;
    /* 事件: 断连  */
    case XF_BLE_GAP_EVT_DISCONNECT: {
        XF_LOGI(TAG, "EV:peer disconnect:s_app_id:%d,conn_id:%d,reason:%u,"
                "addr_type:%d,addr:"XF_BLE_ADDR_PRINT_FMT,
                s_app_id, param->disconnect.conn_id,
                param->disconnect.reason,
                param->disconnect.peer_addr.type,
                XF_BLE_ADDR_EXPAND_TO_ARG(param->disconnect.peer_addr.addr));
        XF_LOGI(TAG, "It will restart ADV");
        xf_ble_gap_start_adv(s_adv_id, DEFAULT_BLE_GAP_ADV_DURATION_FOREVER);
    } break;
    default:
        break;
    }

    return XF_OK;
}

static xf_err_t sample_ble_gatts_event_cb(
    xf_ble_gatts_evt_t event,
    xf_ble_gatts_evt_cb_param_t *param)
{
    UNUSED(s_app_id);
    UNUSED(param);
    switch (event) {
    /* 事件: 读请求  */
    case XF_BLE_GATTS_EVT_READ_REQ: {
        XF_LOGI(TAG, "EV:RECV READ_REQ:s_app_id:%d,conn_id:%d,need_rsp:%d,attr_handle:%d",
                s_app_id, param->read_req.conn_id, param->read_req.need_rsp,
                param->read_req.handle);

        xf_ble_gatts_ntf_t ntf_param = {
            .value = read_req_indication,
            .value_len = sizeof(read_req_indication),
            .handle = param->read_req.handle
        };
        xf_err_t ret = xf_ble_gatts_send_notification(
                           s_app_id, param->read_req.conn_id, &ntf_param);
        XF_CHECK(ret != XF_OK, ret, TAG,
                 "send_notify_indicate failed:%#X", ret);
    } break;
    /* 事件: 写请求  */
    case XF_BLE_GATTS_EVT_WRITE_REQ: {
        XF_LOGI(TAG, "EV:RECV WRITE_REQ:s_app_id:%d,conn_id:%d,need_rsp:%d,attr_handle:%d",
                s_app_id, param->write_req.conn_id, param->write_req.need_rsp,
                param->write_req.handle);
        XF_LOG_BUFFER_HEXDUMP(param->write_req.value, param->write_req.value_len);

        /* 如果是需要响应的请求类型->返回响应 */
        if (param->write_req.need_rsp == true) {
            xf_ble_gatts_ntf_t ntf_param = {
                .value = write_req_indication,
                .value_len = sizeof(write_req_indication),
                .handle = param->write_req.handle,
            };
            xf_err_t ret = xf_ble_gatts_send_notification(
                               s_app_id, param->write_req.conn_id, &ntf_param);
            XF_CHECK(ret != XF_OK, ret, TAG,
                     "send_notify_indicate failed:%#X s_app_id:%d", ret);
        }
    } break;
    /* 事件: MTU 协商  */
    case XF_BLE_GATTS_EVT_EXCHANGE_MTU: {
        XF_LOGI(TAG, "EV:mtu changed:s_app_id:%d,conn_id:%d,mtu_size:%d",
                s_app_id, param->mtu.conn_id, param->mtu.mtu_size);
    } break;
    default:
        break;
    }

    return XF_OK;
}
